package com.example.haoqisystem.Utils.excelUtils;

import com.example.haoqisystem.Annotation.ExcelAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

import static org.apache.poi.ss.usermodel.CellType.STRING;

/**
 * 导入工具类
 * @author 陈俊曦
 * @date 2023/9/9
 * @param <T>
 * 本工具类基于注解驱动，使用时需要在实体类的字段上加上注解，注解的参数为导出时的列名
 */
@Slf4j
public class ImportExcel<T> {
    Class<T> clazz;

    public ImportExcel(Class<T> clazz) {
        this.clazz = clazz;
    }

    public Collection<T> importExcel(File file, String... pattern) {
        Collection<T> dist = new ArrayList<T>();
        try {
            /**
             * 类反射得到调用方法
             */
            // 得到目标目标类的所有的字段列表
            Field filed[] = clazz.getDeclaredFields();
            // 将所有标有Annotation的字段，也就是允许导入数据的字段,放入到一个map中
            Map<String, Method> fieldmap = new HashMap<String, Method>();
            // 循环读取所有字段
            for (int i = 0; i < filed.length; i++) {
                Field f = filed[i];
                // 得到单个字段上的Annotation
                ExcelAnnotation exa = f.getAnnotation(ExcelAnnotation.class);
                // 如果标识了Annotationd的话
                if (exa != null) {
                    // 构造设置了Annotation的字段的Setter方法
                    String fieldname = f.getName();
                    String setMethodName = "set" + fieldname.substring(0, 1).toUpperCase()
                            + fieldname.substring(1);
                    // 构造调用的method，
                    Method setMethod = clazz.getMethod(setMethodName, new Class[] {f.getType()});
                    // 将这个method以Annotaion的名字为key来存入。
                    fieldmap.put(exa.exportName(), setMethod);
                }
            }
            /**
             * excel的解析开始
             */
            // 将传入的File构造为FileInputStream;
            FileInputStream in = new FileInputStream(file);
            // // 得到工作表
            Workbook book = WorkbookFactory.create(in);
//            try {
//                book = new XSSFWorkbook(in);
//            } catch (Exception ex) {
//                book = new HSSFWorkbook(in);
//            }
            // HSSFWorkbook book = new HSSFWorkbook(in);
            // // 得到第一页
            // HSSFSheet sheet = (HSSFSheet) book.getSheetAt(0);
            Sheet sheet = book.getSheetAt(0);
            // // 得到第一面的所有行
            Iterator<Row> row = sheet.rowIterator();

            /**
             * 标题解析
             */
            // 得到第一行，也就是标题行
            Row title = row.next();
            // 得到第一行的所有列
            Iterator<Cell> cellTitle = title.cellIterator();
            // 将标题的文字内容放入到一个map中。
            Map<Integer, String> titlemap = new HashMap<Integer, String>();
            // 从标题第一列开始
            int i = 0;
            // 循环标题所有的列
            while (cellTitle.hasNext()) {
                Cell cell = cellTitle.next();
                String value = cell.getStringCellValue();
                titlemap.put(i, value);
                i = i + 1;
            }
            /**
             * 解析内容行
             */
            // 用来格式化日期的DateFormat
            SimpleDateFormat sf;
            if (pattern.length < 1) {
                sf = new SimpleDateFormat("yyyy-MM-dd");
            } else
                sf = new SimpleDateFormat(pattern[0]);
            while (row.hasNext()) {
                // 标题下的第一行
                Row rown = row.next();

                // 行的所有列
                Iterator<Cell> cellbody = rown.cellIterator();
                // 得到传入类的实例
                T tObject = clazz.newInstance();
                int k = 0;
                // 遍历一行的列
                while (cellbody.hasNext()) {
                    Cell cell = cellbody.next();
                    // 这里得到此列的对应的标题
                    String titleString = (String) titlemap.get(k);
                    // 如果这一列的标题和类中的某一列的Annotation相同，那么则调用此类的的set方法，进行设值
                    if (fieldmap.containsKey(titleString)) {
                        Method setMethod = (Method) fieldmap.get(titleString);
                        // 得到setter方法的参数
                        Type[] ts = setMethod.getGenericParameterTypes();
                        // 只要一个参数
                        String xclass = ts[0].toString();
                        // 判断参数类型

                        cell.setCellType(STRING);
                        if (xclass.equals("class java.lang.String")) {
                            setMethod.invoke(tObject, cell.getStringCellValue());
                        } else if (xclass.equals("class java.util.Date")) {
                            Date parse ;
                            try {
                                parse = sf.parse(cell.getStringCellValue());
                            }catch (Exception e){
                                parse = null;
                            }
                            setMethod.invoke(tObject,parse);
                        } else if (xclass.equals("class java.lang.Boolean")) {
                            Boolean boolname = true;
                            if (cell.getStringCellValue().equals("否")) {
                                boolname = false;
                            }
                            setMethod.invoke(tObject, boolname);
                        } else if (xclass.equals("class java.lang.Integer")) {
                            Integer number;
                            try {
                                number = Integer.valueOf(cell.getStringCellValue());
                            }catch (Exception e){
                                number = null;
                            }
                            setMethod.invoke(tObject, number);
                        }else if (xclass.equals("class java.lang.Double")) {
                            Double number;
                            try {
                                number = Double.valueOf(cell.getStringCellValue());
                            }catch (Exception e){
                                number = null;
                            }
                            setMethod.invoke(tObject, number);
                        }else if (xclass.equals("class java.math.BigDecimal")) {
                            BigDecimal number;
                            try {
                                number = new BigDecimal(cell.getStringCellValue());
                            }catch (Exception e){
                                number = null;
                            }
                            setMethod.invoke(tObject, number);
                        }

                        else if (xclass.equals("class java.lang.Long")) {
                            Long number;
                            try {
                                number = new Long(cell.getStringCellValue());
                            }catch (Exception e){
                                number = null;
                            }
                            setMethod.invoke(tObject, number);
                            setMethod.invoke(tObject,number);
                        }
                    }
                    // 下一列
                    k = k + 1;
                }
                dist.add(tObject);
            }
        } catch (Exception e) {
            log.error(e.toString());
            return null;
        }
        return dist;
    }


}
